﻿// Inner Fire 游戏引擎库
// SkinMesh - 用于载入模型
//
// Copyright (c) 2025 by 尔西大帝. All rights reserved.
//
// 所属层级：应用层(4)
// 用于模型载入。
//
// Author: 尔西大帝
// Email: 2670613871@qq.com
// Created: 2025-01-17

#pragma once

#include <assimp/Importer.hpp>
#include <assimp/postprocess.h> // 包含Assimp的后处理步骤
#include <assimp/scene.h>       // 包含Assimp的场景结构体
#include <dx/mesh/dx_mesh.h>
#include <game/resource/resource.h>
#include <map>
#include <xml_reader.h>
#include <game/resource/model_material.h>
#include <game/animator/armature.h>
#include <dx/core/dx_math.h>
#include <dx/core/vertex.h>
#include <dx/core/skinned_vertex.h>

using namespace ikit;

namespace ifire::game {
class Assets;

constexpr auto MAX_NUM_BONES_PER_VERTEX = 4;

struct VertexBoneData {
  unsigned int BoneIDs[MAX_NUM_BONES_PER_VERTEX] = {0};
  float Weights[MAX_NUM_BONES_PER_VERTEX] = {0.0f};

  VertexBoneData() {}
  void AddBoneData(unsigned int BoneID, float Weight);
};


class SkinMesh : public Resource {
public:
  SkinMesh(Assets* manager, const XmlNode& node);
  SkinMesh(const SkinMesh& lhs) = delete;
  SkinMesh& operator=(const SkinMesh& lhs) = delete;
  ~SkinMesh();

  // 载入此项目
  void Load(ID3D12Device* device, ID3D12GraphicsCommandList* cmd_list);

  //
  // 属性访问
  //
  dx::DxMesh* GetMesh() const { return mesh_.get(); }
  const ModelMaterialInfo& GetMaterialInfo(const std::string_view& name) const;
  const ModelMaterialInfo& GetMaterialInfo(int index) const;
  const std::vector<ModelMaterialInfo>& MaterialsInfo() const {
    return materials_info_;
  }
  size_t GetBoneCount() const { return armature_->bones_.size(); }
  Armature* GetArmature() const { return armature_.get(); }

public:
  int mIndex;
  std::string mName;
  bool mEnable;
  std::vector<XMMATRIX> mAnimeTransform;

private:
  // 重置索引
  void ResetCurrentCount();

  // 载入骨骼
  void LoadBones(const aiScene* scene, ID3D12Device* device,
      ID3D12GraphicsCommandList* cmd_list);

  // 用于轮询节点
  void ProcessNode(aiNode* node, const aiScene* scene, int layer);
  // 将一个SubMesh加入
  void AddSubMesh(aiMesh* mesh, int layer);

  // 总的顶点
  // PS:这里原来我使用std::vector。发现没有本质的速度差别。
  dx::Vertex* vertices_ = nullptr;
  UINT vertex_pointer_ = 0;
  // 总的索引
  std::uint32_t* indices_ = nullptr;
  UINT index_pointer_ = 0;

  // 用于得到当前的节点
  UINT current_vertex_;
  UINT current_index_;

private:
  // 计算总需要的顶点和索引
  void CountVerticesAndIndices();
  // 为顶点分配空间
  void ReserveSpace();
  // 载入所有的网格
  void InitAllMeshes();
  // 载入单个网格
  void InitSingleMesh(unsigned int MeshIndex, const aiMesh* paiMesh);
  void LoadMeshBones(unsigned int MeshIndex, const aiMesh* pMesh);
  void LoadSingleBone(unsigned int MeshIndex, const aiBone* pBone);
  // 设置一个BoneData到Vertex中
  void AddBoneData(
      unsigned int vertex_index, unsigned int bone_id, float weight);
  // 打印所有材质
  void PrintMaterial();

  // std::vector<BasicMeshEntry> m_Meshes;
  unsigned int NumVertices;
  unsigned int NumIndices;
  dx::SkinnedVertex* skinned_vertices_ = nullptr;
  const aiScene* scene_;
  std::vector<VertexBoneData> vertex_to_bones_;
  long long StartTimeMillis;
  ArmaturePtr armature_;

private:
  // 对于渲染器中的引用
  dx::DxMeshPtr mesh_ = nullptr;
  // 此模型对应的文件
  std::string filename_;
  // 载入此模型时，对模型的缩放
  float scale_;
  // 项目的Mesh配置
  std::vector<ModelMaterialInfo> materials_info_;
};

using SkinMeshPtr = std::unique_ptr<SkinMesh>;

} // namespace ifire::game